﻿using System;
using System.Configuration;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.Services;
using System.Runtime.Serialization.Json;
using System.Web.Script.Services;
using System.Data.SqlClient;
using System.Data;
using System.Data.Common;
using System.Web.Script.Serialization;
using System.Text;
using System.IO;
using simplify.json.response;
using simplify.db;
using simplify.json;
using System.Runtime.Remoting.Contexts;
using System.Web.Caching;
using System.Web.Configuration;

public partial class Default : System.Web.UI.Page
{

#region Data Sources
    protected const string APP_DATA_SOURCE_KEY = "app-data";
#endregion

#region Names for stored procedures
    protected const string GET_ALLOWED_LIST_PROC = "get_allowed_list";
    protected const string ALLOW_ENTRY_PROC = "allow_entry";
    protected const string DISALLOW_ENTRY_PROC = "disallow_entry";
#endregion

#region Parameter names for stored procedures
    //get_allowed_list
    protected const string PAGE_NUMBER_PARAM_KEY = "@pageNumber";
    protected const string RECORDS_PER_PAGE_PARAM_KEY = "@recordsPerPage";
    protected const string NPA_FILTER_PARAM_KEY = "@NPAFilter";
    protected const string STATE_FILTER_PARAM_KEY = "@stateFilter";
    protected const string TOTAL_RESULTS_PARAM_KEY = "@totalResults";

    //allow_entry, disallow_entry
    protected const string NPA_PARAM_KEY = "@npa";
    protected const string NXX_PARAM_KEY = "@nxx";
    protected const string XXXX_PARAM_KEY = "@xxxx";
    
#endregion

#region Names of columns returned from the database
    //get_allowed_list
    protected const string NPA_FIELD_KEY = "NPA";
    protected const string NXX_FIELD_KEY = "NXX";
    protected const string XXXX_FIELD_KEY = "XXXX";
    protected const string CITY_FIELD_KEY = "CITY";
    protected const string STATE_FIELD_KEY = "STATE";
#endregion

#region Application Settings Keys
    protected const string ABSOLUTE_EXPIRATION_KEY = "absoluteExpirationSeconds";
    protected const string SLIDING_EXPIRATION_KEY = "slidingExpirationSeconds";
#endregion

    //Entries cached by GetAllowedList() will depend on this. This dependency can then be modified to clear out all dependent entries.
    protected const string GET_ALLOWED_LIST_DEPENDENCY_KEY = "getallowedlist";
    //protected static readonly CacheDependency GetAllowedListDependency = new CacheDependency(null, new string[] { GET_ALLOWED_LIST_DEPENDENCY_KEY });

#region Properties

    /// <summary>
    /// The absolution expiration time in seconds to use for server-side caching of data retrieved from the database
    /// </summary>
    public static DateTime AbsoluteExpiration {
        get
        {
            //Get the setting from the Web.config file
            string strAbsoluteExpirationSeconds = WebConfigurationManager.AppSettings[ABSOLUTE_EXPIRATION_KEY];
            double absoluteExpirationSeconds = 0;
            //If the setting is invalid or not provided
            if (!double.TryParse(strAbsoluteExpirationSeconds, out absoluteExpirationSeconds))
            {
                //Default the no absolute expiration
                return Cache.NoAbsoluteExpiration; 
            }
            //Otherwise add the number of settings to the current timestamp
            return DateTime.UtcNow.AddSeconds(absoluteExpirationSeconds);
        }
    }

    /// <summary>
    /// The sliding expiration time in seconds to use for server-side caching of data retrieved from the database
    /// </summary>
    public static TimeSpan SlidingExpiration
    {
        get
        {
            //Get the setting from the Web.config file
            string strSlidingExpirationSeconds = WebConfigurationManager.AppSettings[SLIDING_EXPIRATION_KEY];
            int slidingExpirationSeconds = 0;
            //If the setting is invalid or not provided
            if (!int.TryParse(strSlidingExpirationSeconds, out slidingExpirationSeconds))
            {
                //Default the no sliding expiration
                return Cache.NoSlidingExpiration;
            }
            //Otherwise use the setting given
            return new TimeSpan(0, 0, slidingExpirationSeconds);
        }
    }

#endregion

    /// <summary>
    /// Page Method for getting pages of the allowed list from the database. Calls the get_allowed_list stored procedure.
    /// </summary>
    /// <param name="pageNumber">The page number to get from the database</param>
    /// <param name="recordsPerPage">The number of records per page for pagination calculations</param>
    /// <param name="NPAFilter">An NPA value to filter results on</param>
    /// <param name="stateFilter">A State value to filter results on. Only the first two characters will get used if there are more</param>
    /// <returns></returns>
    [WebMethod]
    public static GetAllowedEntriesResponse GetAllowedList(int pageNumber, int recordsPerPage, short? NPAFilter, string stateFilter)
    {
        //Generate a cache name to use for this request
        string cacheName = String.Format("{0},{1},{2},{3}", pageNumber, recordsPerPage, NPAFilter, stateFilter);

        Cache cache = HttpContext.Current.Cache;

        //Check the cache first
        GetAllowedEntriesResponse response = (GetAllowedEntriesResponse)cache[cacheName];

        //If cache hit
        if (response != null)
        {
            return response;
        }
        //If cache miss
        else
        {
            //Initialize a response
            response = new GetAllowedEntriesResponse() { RecordsPerPage = recordsPerPage, NPAFilter = NPAFilter, StateFilter = stateFilter };

            try
            {
                //Generate a list of parameters
                List<SqlParameter> sqlParamList = new List<SqlParameter>();

                //@pageNumber parameter
                SqlParameter pageNumberParam = new SqlParameter()
                {
                    ParameterName = PAGE_NUMBER_PARAM_KEY,
                    Direction = ParameterDirection.InputOutput,
                    DbType = DbType.Int32,
                    Value = pageNumber
                };
                sqlParamList.Add(pageNumberParam);

                //@recordsPerPageParameter
                sqlParamList.Add(new SqlParameter()
                {
                    ParameterName = RECORDS_PER_PAGE_PARAM_KEY,
                    Direction = ParameterDirection.Input,
                    DbType = DbType.Int32,
                    Value = recordsPerPage
                });

                //@NPAfilter parameter
                sqlParamList.Add(new SqlParameter()
                {
                    ParameterName = NPA_FILTER_PARAM_KEY,
                    Direction = ParameterDirection.Input,
                    DbType = DbType.Int16,
                    Value = NPAFilter
                });

                //@stateFilter parameter
                //Add the state filter if one has been provided
                if (!String.IsNullOrWhiteSpace(stateFilter))
                {
                    sqlParamList.Add(new SqlParameter()
                    {
                        ParameterName = STATE_FILTER_PARAM_KEY,
                        Direction = ParameterDirection.Input,
                        DbType = DbType.String,
                        Value = stateFilter
                    });
                }

                //@totalResults parameter
                SqlParameter totalResultsParam = new SqlParameter()
                {
                    ParameterName = TOTAL_RESULTS_PARAM_KEY,
                    Direction = ParameterDirection.Output,
                    DbType = DbType.Int32
                };
                sqlParamList.Add(totalResultsParam);

                DataSet dataSet = null;

                //Call the stored procedure and store the return code
                response.ResponseCode = DBAgent.CallQueryProc(APP_DATA_SOURCE_KEY, GET_ALLOWED_LIST_PROC, ref dataSet, sqlParamList.ToArray());

                //Get the output parameters from the procedure
                response.TotalResults = (totalResultsParam.Value != null && totalResultsParam.Value != DBNull.Value ? (int)totalResultsParam.Value : 0);
                response.PageNumber = (pageNumberParam.Value != null && pageNumberParam.Value != DBNull.Value ? (int)pageNumberParam.Value : pageNumber);

                //Populate the response with the data
                foreach (DataRow dataRow in dataSet.Tables[0].Rows)
                {
                    response.AllowedEntries.Add(new AllowedEntry()
                    {
                        NPA = dataRow.Field<short?>(NPA_FIELD_KEY),
                        NXX = dataRow.Field<short?>(NXX_FIELD_KEY),
                        XXXX = dataRow.Field<short?>(XXXX_FIELD_KEY),
                        City = dataRow.Field<string>(CITY_FIELD_KEY),
                        State = dataRow.Field<string>(STATE_FIELD_KEY)
                    });
                }

                //If the GetAllowedList dependency key does not exist yet
                if (cache[GET_ALLOWED_LIST_DEPENDENCY_KEY] == null)
                {
                    //We need to put it in or we are unable to cache anything
                    cache[GET_ALLOWED_LIST_DEPENDENCY_KEY] = DateTime.UtcNow;
                }

                //Since everything completed successfully, cache the response with the given expiration settings and using the appropriate dependency for this operation
                cache.Insert(cacheName, response, new CacheDependency(null, new string[] { GET_ALLOWED_LIST_DEPENDENCY_KEY }), AbsoluteExpiration, SlidingExpiration);
            }
            //If an exception occurs, load the information into the response
            catch (Exception exception)
            {
                response.ResponseCode = -1;
                response.ExceptionMessage = exception.Message;
                response.StackTrace = exception.StackTrace;
            }

            return response;
        }
    }

    /// <summary>
    /// Page Method for adding an entry to the allowed list. Calls the allow_entry stored procedure.
    /// </summary>
    /// <param name="npa">The NPA of the ANI/exchange to add</param>
    /// <param name="nxx">The NXX of the ANI/exchange to add</param>
    /// <param name="xxxx">The XXXX of the ANI to add, or null if adding an exchange</param>
    /// <returns>A response object in JSON form</returns>
    [WebMethod]
    public static ModifyAllowedEntriesResponse AllowEntry(short npa, short nxx, short? xxxx)
    {
        ModifyAllowedEntriesResponse response = new ModifyAllowedEntriesResponse() { NPA = npa, NXX = nxx, XXXX = xxxx };

        try
        {
            //Generate a list of parameters
            List<SqlParameter> sqlParamList = new List<SqlParameter>();

            //@npa parameter
            sqlParamList.Add(new SqlParameter()
            {
                ParameterName = NPA_PARAM_KEY,
                Direction = ParameterDirection.Input,
                DbType = DbType.Int16,
                Value = npa
            });

            //@nxx parameter
            sqlParamList.Add(new SqlParameter()
            {
                ParameterName = NXX_PARAM_KEY,
                Direction = ParameterDirection.Input,
                DbType = DbType.Int16,
                Value = nxx
            });

            //@xxxx parameter
            sqlParamList.Add(new SqlParameter()
            {
                ParameterName = XXXX_PARAM_KEY,
                Direction = ParameterDirection.Input,
                DbType = DbType.Int16,
                Value = xxxx
            });

            //Call allow_entry procedure and store the return code
            response.ResponseCode = DBAgent.CallNonQueryProc(APP_DATA_SOURCE_KEY, ALLOW_ENTRY_PROC, sqlParamList.ToArray());

            //Update the cache dependency in order to invalidate all the previous entries
            HttpContext.Current.Cache[GET_ALLOWED_LIST_DEPENDENCY_KEY] = DateTime.UtcNow;
        }
        //If an exception occurs, load the information into the response
        catch (Exception exception)
        {
            response.ResponseCode = -1;
            response.ExceptionMessage = exception.Message;
            response.StackTrace = exception.StackTrace;
        }
        return response;
    }

    /// <summary>
    /// Page Method for removing an entry from the allowed list. Calls the disallow_entry stored procedure.
    /// </summary>
    /// <param name="npa">The NPA of the ANI/exchange to remove</param>
    /// <param name="nxx">The NXX of the ANI/exchange to remove</param>
    /// <param name="xxxx">The XXXX of the ANI to remove, or null if removeing an exchange</param>
    /// <returns>A response object in JSON form</returns>
    [WebMethod]
    public static ModifyAllowedEntriesResponse DisallowEntry(short npa, short nxx, short? xxxx)
    {
        ModifyAllowedEntriesResponse response = new ModifyAllowedEntriesResponse() { NPA = npa, NXX = nxx, XXXX = xxxx };

        try
        {
            //Build parameters for the procedure
            List<SqlParameter> sqlParamList = new List<SqlParameter>();

            //@npa parameter
            sqlParamList.Add(new SqlParameter()
            {
                ParameterName = NPA_PARAM_KEY,
                Direction = ParameterDirection.Input,
                DbType = DbType.Int16,
                Value = npa
            });

            //@nxx parameter
            sqlParamList.Add(new SqlParameter()
            {
                ParameterName = NXX_PARAM_KEY,
                Direction = ParameterDirection.Input,
                DbType = DbType.Int16,
                Value = nxx
            });

            //@xxxx parameter
            sqlParamList.Add(new SqlParameter()
            {
                ParameterName = XXXX_PARAM_KEY,
                Direction = ParameterDirection.Input,
                DbType = DbType.Int16,
                Value = xxxx
            });

            //Call disallow_entry procedure and store the return code
            response.ResponseCode = DBAgent.CallNonQueryProc(APP_DATA_SOURCE_KEY, DISALLOW_ENTRY_PROC, sqlParamList.ToArray());

            //Update the cache dependency in order to invalidate all the previous entries
            HttpContext.Current.Cache[GET_ALLOWED_LIST_DEPENDENCY_KEY] = DateTime.UtcNow;
        }
        //If an exception occurs, load the information into the response
        catch (Exception exception)
        {
            response.ResponseCode = -1;
            response.ExceptionMessage = exception.Message;
            response.StackTrace = exception.StackTrace;
        }
        return response;
    }

}